{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "If running in a new enviroment, such as Google Colab, run this first."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# !git clone https://github.com/zach401/acnportal.git\n",
    "# !pip install acnportal/."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# ACN-Sim Tutorial: Lesson 1\n",
    "## Running an Experiment\n",
    "### by Zachary Lee\n",
    "#### Last updated: 03/19/2019\n",
    "\n",
    "In this first lesson we will learn how to setup and run a simulation using a built-in scheduling algorithm. After running the simulation we will learn how to use the analysis subpackage to analyze the results of the simulation."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "import pytz\n",
    "from datetime import datetime\n",
    "\n",
    "import matplotlib.pyplot as plt\n",
    "\n",
    "from acnportal import acnsim\n",
    "from acnportal import algorithms"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Experiment Parameters \n",
    "\n",
    "Next we need to define some parameters of the experiment. We define these at the begining of the file so they can be used consistently when setting up the simulation."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# Timezone of the ACN we are using.\n",
    "timezone = pytz.timezone('America/Los_Angeles')\n",
    "\n",
    "# Start and End times are used when collecting data.\n",
    "start = timezone.localize(datetime(2018, 9, 5))\n",
    "end = timezone.localize(datetime(2018, 9, 6))\n",
    "\n",
    "# How long each time discrete time interval in the simulation should be.\n",
    "period = 5  # minutes\n",
    "\n",
    "# Voltage of the network.\n",
    "voltage = 220  # volts\n",
    "\n",
    "# Default maximum charging rate for each EV battery.\n",
    "default_battery_power = 32 * voltage / 1000 # kW\n",
    "\n",
    "# Identifier of the site where data will be gathered.\n",
    "site = 'caltech'"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Network \n",
    "\n",
    "An important part of any simulation is the ChargingNetwork on which it runs. The ChargingNetwork is a description of the physical system and contains both the set of EVSEs which make up the network as well as a constraint_matrix which represents the electrical infrastructure of the network. You can manually configure this network using the register_evse() and add_constraint() methods in ChargingNetwork or you can use a predefined network available in the sites module. In this case we use the predefined CaltechACN network."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# For this experiment we use the predefined CaltechACN network.\n",
    "cn = acnsim.sites.caltech_acn(basic_evse=True, voltage=voltage)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Events\n",
    "\n",
    "Events are what drive action in the simulator. Events are stored in an EventQueue. This queue can be built manually by creating an Event object and using the add_event() or add_events() methods, or can be generated automatically.\n",
    "\n",
    "In this case we will use acndata_events.generate_events() which is part of the events subpackage. acnevents provides utilities for generating events from the Caltech Charging Dataset. These events are based on real behavior of users charging actual EVs, so it is extremely valuable for running realistic simulations. In order to access the API we need a token. For now we can use the demo token, but it is highly recomended that you register for your own free token at ev.caltech.edu."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "API_KEY = 'DEMO_TOKEN'\n",
    "events = acnsim.acndata_events.generate_events(API_KEY, site, start, end, period, voltage, default_battery_power)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Scheduling Algorithm\n",
    "\n",
    "The primary purpose of acnportal is to evaluate scheduling algorithms for large-scale EV charging. We will discuss how develop your own custom algorithm in Lesson 2, for now we will use one of the builtin scheduling algorithms, UncontrolledCharging."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "sch = algorithms.UncontrolledCharging()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Simulator\n",
    "\n",
    "We next need to set up our simulation enviroment using the parts we have already defined. The Simulator constructor takes in a ChargingNetwork, Algorithm, and EventQueue. We also provide the start time of the simulation which all internal timestamps will be measured relative to. Finally we pass in the length of each period as well as a parameter called max_recomp. max_recomp controls how often the scheduling algorithm is called when no events occur. Here we have set max_recomp to 1, meaning the scheduling algorithm will be called every time step. If we had set it to 5, up to 5 time steps could occur before the scheduling algorithm was called. Note that the scheduling algorithm is always called when an event occurs. In this case, UncontrolledCharging only provides one charging rate, so it must be used with a max_recomp of 1."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "sim = acnsim.Simulator(cn, sch, events, start, period=period, verbose=False)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "To execute the simulation we simply call the run() function."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "sim.run()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Analysis\n",
    "\n",
    "Once the simulator has been run, we can analyze the results. For this purpose acnsim offers a package called analysis. One thing we may be interested in is the proportion of total users’ energy demand that we were able to meet. To find this we can use the proportion_of_energy_delivered() method from the analysis subpackage. The only argument to this function is the Simulator object itself.\n",
    "\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "total_energy_prop = acnsim.proportion_of_energy_delivered(sim)\n",
    "print('Proportion of requested energy delivered: {0}'.format(total_energy_prop))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "We may also be interested in the peak demand of the system as this determines our big the root transformers and cables in our system must be as well as the demand charge we may have to pay. The Simulator has a built in property which keeps track of this peak usage called peak."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "print('Peak aggregate current: {0} A'.format(sim.peak))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Finally, we can plot the output of our simulation. For now we will just plot total aggregate current draw:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# Plotting aggregate current\n",
    "agg_current = acnsim.aggregate_current(sim)\n",
    "plt.plot(agg_current)\n",
    "plt.xlabel('Time (periods)')\n",
    "plt.ylabel('Current (A)')\n",
    "plt.title('Total Aggregate Current')\n",
    "plt.show()"
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.6.3"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 2
}
